/*

	O max6675 é um conversor analógico para digital para os sensores termopares tipo K.
	Este conversor é de 12 bits e com 0.25 de resolução, e já faz a compensação de junção a frio.
	A porta de comunicação do max é a SPI com velocidade máxima de 4.3MHz no modo padrão

	ATENÇÃO: Quando se faz a leitura no max, no momento de CS=0 para qualquer conversão no CI,
	e inicia uma nova conversão quando CS for a 1.
	Logo o intervalo de leituras deixar pelo menos 200ms

	Este gerenciador pode controlar muitos sensores, se usar mais de um utilize
		a sequencia de seleção dos chipselect de 1 a até o limite

*/

#include "max6675.h"
#include <stdlib.h>
#if (MAX_USE_DEBUG == pdON)
#include "stdio_uc.h"
#endif

typedef struct {
	volatile uint time;	// Contagem de intervalo de tempo entre leituras de cada sensor
	uint rx; 	// Valor lido do max
	uint buff[MAX6675_N_SAMPLY]; // buffer com os valores lidos do max
	uint index; // Index do buffer
	int status;	// Status do sensor
				// errSENSOR_NO_CONNECTED
				// errSENSOR_INITIALIZING
				// errDEVICE_NOT_FOUND
				// pdOK
	uint value; // Valor médio das leituras dos sensores
} tMax;

static tMax max[MAX6675_N_SENSORS];

//------------------------------------------------------------------------------------------------------------------
// Descrição: Lê o ADC e já informa o status do processo
// Parametros: Qual dispositivo deseja ler, valor de 0 ao valor máximo
// Retorna: Nada
//------------------------------------------------------------------------------------------------------------------
static void max6675_Transfer(int dev) {
    max6675_Enable(dev);
	max[dev].rx =  max6675_TransferByte(0x0);	// Lê o dado mais significativo
	max[dev].rx <<= 8;
	max[dev].rx |= max6675_TransferByte(0x0);	// Lê o dado menos significativo
    max6675_Disable(dev);

    if (max[dev].rx >= 65535)   max[dev].status = errDEVICE_NOT_FOUND;
    else if (max[dev].rx & 0x4) max[dev].status = errSENSOR_NO_CONNECTED;
    else 						max[dev].status = pdOK;

	#if (MAX_USE_DEBUG == pdON)
    plognp("MAX dev:%d ctrl: 0x%x"CMD_TERMINATOR, dev, max[dev].rx>>3, max[dev].rx&7);
    #endif

	max[dev].rx >>=3; // retira bits de status e matém só os valores de tempertaratura
}

// Inicializa o buffer de cada ADC
static void max6675_InitMesasures(int dev) {
	int x; for(x=0;x<MAX6675_N_SAMPLY;x++) max[dev].buff[x] = -1;
	max[dev].index = 0;
}

//------------------------------------------------------------------------------------------------------------------
// Descrição: Inicializa gerenciador de controle de acesso aos sensores
// Parametros	Nenhum
// Retorna: 	Nada
//------------------------------------------------------------------------------------------------------------------
void max6675_Init(void) {
	int x; for(x=0;x<MAX6675_N_SENSORS;x++) {
		max[x].time = 0;
		max[x].status = errSENSOR_INITIALIZING;
		max6675_InitMesasures(x);
	}
	max6675_SetupPorts();	// Configurar os pinos CS
}

// controle de tempos
// esta função deve ser chamada a cada 1ms pelo tick do sistema
void max6675_Tick(void) {
	int x; for(x=0;x<MAX6675_N_SENSORS;x++) {
		if (max[x].time) max[x].time--;
	}
}


// Processo de leitura dos ADCs.
// 	Fazer um por um em cada chamada do process, isto para liberar mais tempo para outros processos
static int device = 0;

void max6675_Process(void) {
	// não faz nada se ainda não deu intervalo de leitura ao ADC atual
	if (max[device].time) return;
	max[device].time = MAX6675_TIME_SAMPLE;

	// obter o valor do adc
	max6675_Transfer(device);

	// aplicar filtro
	if (max[device].status != pdOK) {
		max6675_InitMesasures(device);
	// vamos desconsiderar a primeira leitura, porque o sensor
	} else {
		max[device].buff[max[device].index % MAX6675_N_SAMPLY] = max[device].rx;

		if (max[device].index > 0) {
			int qtd;
			if (max[device].index < MAX6675_N_SAMPLY) qtd = max[device].index+1; else qtd = MAX6675_N_SAMPLY;

			// media normal
			int m = 0;
			int i; for (i=0;i<qtd;i++) m += max[device].buff[i];
			m = m/qtd;

			// vamos remover o ponto discrepante
			int x;
			int k, d;
			k = max[device].index % qtd; //automaticamente considera o último ponto como sendo ruim
			//k = max[device].index % MAX6675_N_SAMPLY; //automaticamente considera o último ponto como sendo ruim
			d = 0; // não tão ruim assim, por consideramos aqui com desvio "0" da media

			// procuramos pelo ponto mais longe
			for (i=0;i<qtd;i++) {
				x = abs(m - max[device].buff[i]); 	// qual a diferença deste ponto para media?
				if (x<d) continue;        			// é menor que o outro? entao esquece e vai para o proximo
				k = i;								// marca o ponto "i" como sendo o pior de todos ate agora
				d = x;								// anota quao ruim ele é
			}

			// vamor calcular novamente a media sem o ponto ruim
			m = 0;
			for (i=0;i<qtd;i++) if (i!=k) m += max[device].buff[i];
			m = m/(qtd-1); // -1 porque sabemos que foi retirado apenas 1 ponto

			max[device].value = m;
		} else 	max[device].value = max[device].rx;

		max[device].index++;
	}

	// apontar para próximo ADC
	if (device < MAX6675_N_SENSORS-1) device++; else device=0;
}

//------------------------------------------------------------------------------------------------------------------
// Descrição: Lê a temperatura no max6675
// Parametros: *value: Ponteiro para o retorno do valor lido
//				type: tempCELCIUS ou tempFAHRENHEIT 		1
// Retorna: 	pdOK: Se foi lido com sucesso
//				errSENSOR_NO_CONNECTED: Caso o sensor estiver desconecatado
//				errDEVICE_NOT_FOUND
// 				errSENSOR_INITIALIZING
//				errDEVICE_INVALID
//------------------------------------------------------------------------------------------------------------------
int max6675_ReadTemp(int dev, double* value, int type) {
	if (dev > MAX6675_N_SENSORS-1) return errDEVICE_INVALID;

	*value = max[dev].value*0.25;
	// Se for grau Fahrenheit converte
	if (type) *value = *value * (9.0/5.0) + 32;

	return max[dev].status;
}
